using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using SpoonCore.Message.Info.Core;

namespace SpoonCore.Message.Info
{
	public class InfoMessageHandler
	{
		private SortedList<int, IHandlerItem> _handlerList = new SortedList<int, IHandlerItem>();
		private SortedList<int, AbstractMessageInfo> _prototypeList = new SortedList<int, AbstractMessageInfo>();

		public delegate void OnInfoReceiveHandler<T>(T info, IPEndPoint source) where T : AbstractMessageInfo;
		public delegate void OnInfoSendHandler<T>(IMessage msg, IPEndPoint dest, T info) where T : AbstractMessageInfo;
		
		public InfoMessageHandler(SpoonClient client)
		{
			new CoreInfoHandler(this, client);
		}

		public void RegisterHandler<T>(OnInfoReceiveHandler<T> handler, OnInfoSendHandler<T> sendHandler) where T : AbstractMessageInfo, new()
		{
			T tmp = new T();
			_handlerList.Add(tmp.InfoId, new HandlerItem<T>(handler, sendHandler));
			_prototypeList.Add(tmp.InfoId, tmp);
		}

		private interface IHandlerItem
		{
			void DataReceive(AbstractMessageInfo msg, IPEndPoint source);
			void DataSend(IMessage msg, IPEndPoint endpoint, AbstractMessageInfo info);
		}

		private class HandlerItem<T> : IHandlerItem where T : AbstractMessageInfo
		{
			public HandlerItem(OnInfoReceiveHandler<T> handler, OnInfoSendHandler<T> sendHandler)
			{
				OnMessageReceive += handler;
				OnMessageSend += sendHandler;
			}

			private event OnInfoReceiveHandler<T> OnMessageReceive;
			private event OnInfoSendHandler<T> OnMessageSend;

			public void DataReceive(AbstractMessageInfo msg, IPEndPoint source)
			{
				if (OnMessageReceive != null) OnMessageReceive((T)msg, source);
			}

			public void DataSend(IMessage msg, IPEndPoint endpoint, AbstractMessageInfo info)
			{
				if (OnMessageSend != null) OnMessageSend(msg, endpoint, (T)info);
			}
		}

		internal MessageInfo DataReceive(BinaryReader br, IPEndPoint source)
		{
			int count = br.ReadInt32();
			MessageInfo res = new MessageInfo();
			while (count > 0)
			{
				int id = br.ReadInt32();
				if (_prototypeList.ContainsKey(id))
				{
					AbstractMessageInfo tmp = _prototypeList[id].Clone();
					tmp.SetBytes(br);
					res.Infos.Add(tmp);

					_handlerList[tmp.InfoId].DataReceive(tmp, source);
				}
				count--;
			}

			return res;
		}

		internal void DataSend(IMessage msg, IPEndPoint endpoint)
		{
			if(msg.Info != null)
			{
				foreach (AbstractMessageInfo info in msg.Info.Infos)
				{
					if(_handlerList.ContainsKey(info.InfoId))
					{
						_handlerList[info.InfoId].DataSend(msg, endpoint, info);
					}
				}
			}
		}
	}
}
